home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Technical Documentation / Sample Code / DTS_SCSI Code (In Development) / DTS_SCSI_Format.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-15  |  47.5 KB  |  1,354 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        DTS_SCSI_Format.c
  3.  
  4.     Contains:    SCSI-specific code for DTS' SCSI sample
  5.     
  6.     
  7.     
  8.     
  9.     Unfortunately, no matter how long awaited, it's still not done.  In fact, this
  10. isn't even a release- this is just an image of the code taken in the middle of
  11. development.
  12.  
  13. THIS CODE DOES NOT WORK AS A WHOLE.  MUCH OF IT IS BUGGY AND / OR INCOMPLETE.
  14. YOU WOULD HAVE TO BE ABSOLUTELY INSANE TO USE ANY OF THIS CODE IN YOUR
  15. PROJECT WITHOUT EXTENSIVE THOUGHT, DEBUGGING AND TESTING.
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.     Written by:    Kent Sandvik
  23.  
  24.     Copyright:    © 1991 by Apple Computer, Inc., all rights reserved.
  25.  
  26.     Change History (most recent first):
  27.     
  28.                  07/16/92    khs        added read capacity calls        
  29.                  07/15/92   khs        additional code mangling based on code review
  30.                  04/24/92   ckd        added code for PartitionCurrentDevice()  
  31.                  04/21/92    khs        added changes from code review
  32.                  03/14/92    BJS        changed header, added to project
  33.                  10/19/91    khs        first version
  34.     To Do:
  35. */
  36.  
  37. #define __FILE_NUMBER__ 0x0002
  38.  
  39. #include <PLStringFuncs.h>
  40. #include <Devices.h>
  41. #include <Types.h>
  42. #include <Memory.h>
  43. #include <Resources.h>
  44. #include <Files.h>
  45. #include <OSEvents.h>
  46. #include <DiskInit.h>
  47. #include <Errors.h>
  48. #include <String.h>
  49. #include <ToolUtils.h>
  50. #include <Strings.h>
  51.  
  52. #include "DTS_SCSI_Application.h"
  53. #include "DTS_SCSI_Format.h"
  54. #include "DTS_SCSI_IO.h"
  55. #include "DTS_SCSI_Driver.h"
  56. #include "DTS_SCSI_Debug.h"
  57.  
  58. // bits in dCtlFlags
  59. enum {
  60.     dOpened = 5,
  61.     dRAMBased,
  62.     drvrActive
  63. };
  64.  
  65. struct DriverHeader {
  66.     short    drvrFlags;
  67.     short    drvrDelay;
  68.     short    drvrEMask;
  69.     short    drvrMenu;
  70.     short    drvrOpen;
  71.     short    drvrPrime;
  72.     short    drvrCtl;
  73.     short    drvrStatus;
  74.     short    drvrClose;
  75.     Str255    drvrName;
  76. };
  77. typedef struct DriverHeader DriverHeader;
  78.  
  79. // We cast the driver handle to this "type" to allow us to call it
  80. // (to install the driver).
  81. typedef void (*InstallFunctionPtr)(void);
  82.  
  83. /********************************************************************************************
  84.  *
  85.  * State variables private to the routines in this file.
  86.  *
  87.  ********************************************************************************************/
  88.  
  89. // The current SCSI device (kInvalidDevice if we don't have a device yet)
  90. SCSIAddress gCurrentDevice = kInvalidDevice;
  91.  
  92. // The strings we'll use in validating one of our drives
  93. // This is set up by a call to SetValidationInfo.
  94. Str255 gVendorString;
  95. Str255 gProductString;
  96. Str255 gRevisionString;
  97.  
  98. /********************************************************************************************
  99.  * To call the driver to install itself, we need to pass parameters in
  100.  * a couple of oddball registers. If I wasn't so lazy, I'd create a
  101.  * little assembly-language procedure to save off the important registers
  102.  * & load 'em up with the right parameters, call the driver, then restore
  103.  * the registers. It might look something like this:
  104.  * 0000: 48E7 0500 MOVEM.L    D5/D7,-(A7)  ;save registers
  105.  * 0004: 206F 0008 MOVEA.L    $0008(A7),A0 ;get partition map ptr
  106.  * 0008: 2E2F 000C MOVE.L     $000C(A7),D7 ;get defaultData
  107.  * 000C: 3A2F 0010 MOVE.W     $0010(A7),D5 ;get SCSI address
  108.  * 0010: 226F 0012 MOVEA.L    $0012(A7),A1 ;get driver ptr
  109.  * 0014: 4E91      JSR        (A1)           ;call the driver
  110.  * 0016: 4CDF 00A0 MOVEM.L    (A7)+,D5/D7  ;restore the registers
  111.  * 001A: 4FEF 0014 LEA        $0014(A7),A7 ;clean up the stack
  112.  *
  113.  * Since I'm so lazy, I've created an inline function to do it for me:
  114.  ********************************************************************************************/
  115. pascal void CallDriver(Ptr driver, SCSIAddress device, unsigned long 
  116.     defaultData, Partition *partitionMap) = { 0x48E7, 0x0500, 0x206F,
  117.     0x0008, 0x2E2F, 0x000C, 0x3A2F, 0x0010, 0x226F, 0x0012, 0x4E91,
  118.     0x4CDF, 0x00A0, 0x4FEF, 0x0014 };
  119.  
  120. /********************************************************************************************
  121.  *
  122.  * SCSI utility functions
  123.  *
  124.  * These functions act as the interface between the application's functionality and the
  125.  * low-level SCSI operations. They do specific SCSI-oriented tasks like testing whether a
  126.  * specific SCSI device is ready and ours, writing the empty partition map, etc.
  127.  *
  128.  ********************************************************************************************/
  129.  
  130. //
  131. // We're in the process of validating a drive, and have retrieved its vendor info.
  132. // Compare this Pascal string with as many characters from the drive string - if they
  133. // all match, return 1, else return 0.
  134. int MatchStrings(Str255 testString, char *driveString) {
  135.     char *ours = &testString[1]; // point at the first character of our string
  136.     short matchLen = testString[0]; // use the length of our string as the count to match
  137.     while ((*(ours++) == *(driveString++)) && --matchLen) ;
  138.     return (matchLen == 0); // did it match?
  139. }
  140.  
  141. //
  142. // Check to see if this SCSI address has a device that's ready;
  143. // if so, see if it's one of our devices. Returns 0 if so, or
  144. // the error.
  145. //
  146. OSErr CheckSCSIDevice(SCSIAddress device) {
  147.     OSErr anErr = noErr;
  148.  
  149.     // We'll allow the device a few chances to get ready; this is helpful for
  150.     // drives with "unit attention" turned on (though our device doesn't do
  151.     // this).
  152.     
  153.     anErr = SCSITestUnitReady(device);
  154.     if (anErr != noErr)
  155.         return anErr;
  156.         
  157.     // The device is ready - see if it's ours by asking it for its inquiry record.
  158.     // Because we don't know how big this device's inquiry response will be, we'll
  159.     // issue this command twice: the first time, we'll only request enough to retrieve
  160.     // the response size (5 bytes).
  161.     
  162.     return (SCSIDoInquiryCommand(device));
  163. }
  164.  
  165.  
  166. // See if the device is ready. Since TestUnitReady doesn't involve
  167. // transfer of data, we don't need to pass buffer information. Also, 
  168. // since we know this command doesn't involve retrieving data from the
  169. // drive's media, we use a pretty short timeout (if it fails, we'll 
  170. // retry anyway).
  171.  
  172. OSErr SCSITestUnitReady(SCSIAddress device) {
  173.     SCSICommandBlock testCmd;        // the command block needed for Test Unit Ready
  174.     OSErr     anErr;
  175.     short    retries;
  176.     
  177.     StuffSCSICommandBlock(testCmd, SCSICmd_TestUnitReady, 0, 0, 0, 0, 0);
  178.     retries = 3;
  179.     do {
  180.         // See if the device is ready. Since TestUnitReady doesn't involve
  181.         // transfer of data, we don't need to pass buffer information. Also, 
  182.         // since we know this command doesn't involve retrieving data from the
  183.         // drive's media, we use a pretty short timeout (if it fails, we'll 
  184.         // retry anyway).
  185.         anErr = SCSIOp(testCmd, sizeof(SCSICommandBlock), device, kShortTimeout,
  186.                          kNoBuffer);
  187.     } while ((anErr != noErr) && --retries);
  188.     
  189.     return anErr;
  190. }
  191.  
  192. // Issue an INQUIRY SCSI command to the device, first find out the size of the 
  193. // record sent back, and then fetch the whole record, test for errors as well!
  194.  
  195. OSErr SCSIDoInquiryCommand(SCSIAddress device) {
  196.     SCSICommandBlock inquiryCmd; // our Inquiry command block
  197.     OSErr    anErr;
  198.  
  199.     // Here's the structure that we get back from an INQUIRY command:
  200. #define kVendorIDSize 8
  201. #define kProductIDSize 16
  202. #define kRevisionSize 4
  203.     struct {                                        // see ANSI SCSI standard for more info about these fields
  204.         unsigned char fDeviceType;                    // these two fieds indentifies the device currently 
  205.         unsigned char fDeviceQualifier;                // corrected to the logical unit
  206.         unsigned char fVersion;                        // this defines the version level (vendor + standard)
  207.         unsigned char fResponseFormat;                // defines the used data format (SCSI INQUIRY or something else)        
  208.         unsigned char fAdditionalLength;            // length of bytes of the parameters
  209.         unsigned char fVendorUse1;                    // vendor specific data
  210.         unsigned short fReserved1;
  211.         unsigned char fVendorID[kVendorIDSize];        // vendor ID data
  212.         unsigned char fProductID[kProductIDSize];    // product data
  213.         unsigned char fRevision[kRevisionSize];        // product version data
  214.         unsigned char fVendorUse2[20];                // vendor specific data
  215.         unsigned char fReserved2[42];
  216.         unsigned char fVendorUse3[158];                // vendor specific data
  217.     } inquiryResponse;
  218.     // We don't need to check the string size, because the inquiryResponse block can't be bigger
  219.     // than 256 bytes.
  220.     
  221.     // We have to get at least this much to allow us to compare vendor strings!
  222.     short minimumAdditionalLength = (short) (inquiryResponse.fProductID - &inquiryResponse);
  223.     
  224.     StuffSCSICommandBlock(inquiryCmd, SCSICmd_Inquiry, 0, 0, 0, 5, 0); // ask for 5 bytes
  225.     anErr = SCSIOp(inquiryCmd, sizeof(SCSICommandBlock), device, kShortTimeout, 
  226.                      &inquiryResponse, 5, kNoLoop, kDoRead, kDoPolled);
  227.     // Skip out if we err'd, or if we didn't get enough data back.
  228.     if ((anErr != noErr) || (inquiryResponse.fAdditionalLength < minimumAdditionalLength))  
  229.         return anErr;
  230.     
  231.     // Now that we know how big the response will be, reissue the command and ask for
  232.     // the whole enchilada.
  233.     StuffSCSICommandBlock(inquiryCmd, SCSICmd_Inquiry, 0, 0, 0, 
  234.                     inquiryResponse.fAdditionalLength, 0); // ask for the whole thing
  235.     anErr = SCSIOp(inquiryCmd, sizeof(SCSICommandBlock), device, kShortTimeout, 
  236.                      &inquiryResponse, inquiryResponse.fAdditionalLength, 
  237.                     kNoLoop, kDoRead, kDoPolled);
  238.     if (anErr != noErr)
  239.         return anErr;
  240.  
  241.     // We got the info. If all three strings match, it's good. Otherwise, it's not.
  242.  
  243.     return(SCSIMatchDriveInfo(inquiryResponse.fVendorID, inquiryResponse.fProductID,
  244.                                 inquiryResponse.fRevision));
  245. }
  246.     
  247.  
  248. // Issue a READ CAPACITY command to the device in order to find out the 
  249. // capacity (size) of the disk.
  250. // *** this is Undiscovered Code, boys and girls, please test and fix it ***
  251. unsigned long SCSIDoReadCapacityCommand(SCSIAddress device){
  252.     SCSICommandBlock readcapCmd;
  253.     unsigned long capacity;
  254.     OSErr anErr;
  255.  
  256.     // here's the structure that we get back from a READ CAPACITY call
  257.     unsigned long readcapResponse[2];
  258.     
  259.     // place together the SCSI command block
  260.     StuffSCSICommandBlock(readcapCmd, SCSICmd_ReadCap, 0, 0, 0, 8, 0); // get back 8 bytes
  261.     
  262.     // call the SCSI device
  263.     anErr = SCSIOp(readcapCmd, sizeof(SCSICommandBlock), device, kShortTimeout,
  264.                 &readcapResponse, sizeof(readcapResponse), kNoLoop, kDoRead, kDoPolled);
  265.     
  266.     assert(anErr == noErr, "Problems with READ CAPACITY SCSI call");
  267.     
  268.     // the value returned is the logical address of the last block, so
  269.     // we add 1 to the total number of blocks times blocksize so
  270.     // we will get the amount in bytes. Note that we get the physical block size
  271.     // as well with the READ CAPACITY call.
  272.     
  273.     if (anErr == noErr)
  274.         capacity = (readcapResponse[0] + 1) * readcapResponse[1];
  275.     else
  276.         capacity = 0;        // Indicate error with 0 capacity
  277.  
  278.     return capacity;
  279. }
  280.  
  281.  
  282. // Get the volume name from the specified current SCSI device
  283. // *** Yet again, untested code, please test this out, Kent.
  284. Str31 SCSIGetVolumeName(){
  285.     Str31 volumeName;
  286.     HParamBlockRec hpb;
  287.     OSErr anErr;
  288.     
  289.     hpb.volumeParam.ioNamePtr = volumeName;            // we will get our name here
  290.     hpb.volumeParam.ioVRefNum = ~(32 + gCurrentDevice);    // refnum for the current SCSI device volume
  291.     hpb.volumeParam.ioVolIndex = 0;                // needs to be zero
  292.     
  293.     anErr = PBHGetVInfo(&hpb, false);
  294.     assert(anErr == noErr, "Problems with PBHGetVInfo");
  295.     
  296.     return volumeName;
  297. }
  298.  
  299.  
  300. OSErr SCSIMatchDriveInfo(char* vendor, char* product, char* revision) {
  301. //    We use global string constants in this case, because we want to have 
  302. //    the values locked to a particular drive. However it would make sense
  303. //    to have these strings as resources instead for quick changes
  304.  
  305.     return (MatchStrings(gVendorString, vendor) &&
  306.             MatchStrings(gProductString, product) &&
  307.             MatchStrings(gRevisionString, revision)) ? noErr : -1;
  308. }
  309.  
  310.  
  311. //
  312. // Find the DCE for this SCSI device
  313. // Return NULL if the unit table slot was empty.
  314. //
  315. DCtlHandle DCEFromDevice(SCSIAddress device) {
  316.     DCtlHandle *unitTable = *(DCtlHandle **) 0x11C;
  317.     short unitEntry = (device + 32); // Unit table entries for SCSI start at 32
  318.     return unitTable[unitEntry]; // Get the Device Control Entry for this device
  319. }
  320.  
  321. //
  322. // Find the drive number for this SCSI device
  323. // Return 0 if none found.
  324. //
  325. short DriveFromDevice(SCSIAddress device) {
  326.     short refnum;
  327.     DrvQEl *dqe;
  328.     
  329.     // Convert the device address to a driver refnum,
  330.     // and get the first drive in the queue
  331.     refnum = ~(device + 32);
  332.     dqe = (DrvQEl *) GetDrvQHdr()->qHead;
  333.     
  334.     // Walk the queue until we match our refnum 
  335.     // or run out of drives.
  336.     while (dqe && (dqe->dQRefNum != refnum))
  337.         dqe = (DrvQEl *) dqe->qLink;
  338.     
  339.     // Return 0 if we ran out, or the drive number we found.
  340.     return dqe ? dqe->dQDrive : 0;
  341. }
  342.  
  343. /********************************************************************************************
  344.  *
  345.  * High-level functions
  346.  *
  347.  * These functions are called by the application; they handle most of the user's commands:
  348.  * switch to next drive, format current drive, test drive, etc. They also allow the application
  349.  * level to find out about this level's state (like the ID of the current device, it's volume name
  350.  * if any, etc).
  351.  *
  352.  ********************************************************************************************/
  353.  
  354. //
  355. // Get the ID of the current device
  356. // *** eventually, get the volume name if it has one!
  357. //
  358. SCSIAddress CurrentDevice() {
  359.     return gCurrentDevice;
  360. }
  361.  
  362. //
  363. // Is this device run by no driver, someone else's driver, a current
  364. // version of our driver, or an old version of our driver? We
  365. // only allow the user to mess with this device if it's got no driver
  366. // or some version of our driver; we allow the user to update current
  367. // version of the driver only if it's the old version of our driver.
  368. //
  369. TDriverKind DriverKind(SCSIAddress device) {
  370.     char            *driverName,*driverVersion;
  371.     DCtlHandle        thisDCE;
  372.     
  373.     thisDCE = DCEFromDevice(device);
  374.     
  375.     // If we didn't find a device control entry for this device, there's no driver here.
  376.     if (!thisDCE)
  377.         return kNoDriver;
  378.     
  379.     // There's a driver here. If it's ours, it won't be marked "RAM-based"
  380.     // (the flag really determines whether dCtlDriver is a handle or pointer;
  381.     // for us, it's always a pointer).
  382.     
  383.     if ((*thisDCE)->dCtlFlags & (1 << dRAMBased))
  384.         return kDifferentDriver;
  385.         
  386.     // The driver that's here isn't marked RAM-based, so it might be ours.
  387.     // Check its name and version.
  388.     
  389.     driverName = ((DriverHeader*) (*(*thisDCE)->dCtlDriver))->drvrName;
  390.     
  391.     if ( PLstrcmp( kDriverName, driverName ) != 0)
  392.         return kDifferentDriver;
  393.     
  394.     // The version is one byte immediately following the name
  395.     // Advance the name pointer by the length of the name + 1 for the length byte
  396.     
  397.     driverVersion = driverName + (*driverName) + 1;
  398.     
  399.     if (*driverVersion == kCurrentDriverVersion)
  400.         return kOurCurrentDriver;
  401.     else
  402.         return kOurOldDriver;
  403. }
  404.  
  405.  
  406. //
  407. // Get the volume reference number and name of the volume mounted on this
  408. // SCSI device (if any), in order to tell the user which volume will be
  409. // trashed in formatting. Assumes that we've already checked to see that
  410. // this device has some version of our driver controlling it (of course,
  411. // if it had no driver, it couldn't have a volume; likewise, if it had
  412. // some other driver, we couldn't format it anyway).
  413. //
  414. // For us, it's simple: we only support one volume per device: if we find
  415. // a volume that uses our refnum, we return its volume reference number
  416. // (were we a multiple logical-unit-number device, we'd have to investigate 
  417. // further to make sure that the volume was really the one we planned on 
  418. // messing with -- we'd have to pass a logical unit number to this routine as
  419. // well).
  420. //
  421. //
  422. // Return 0 if no volume mounted.
  423. //
  424. short VolumeFromDevice(SCSIAddress device, Str255 name) {
  425.     DCtlHandle thisDCE;
  426.     short anErr, index;
  427.     HVolumeParam pb;
  428.     
  429.     // Find the device control entry that goes with this device.    
  430.     thisDCE = DCEFromDevice(device);
  431.     if (!thisDCE)
  432.         return 0; // No DCE, no volume, no shirt, no service.
  433.  
  434.     // Look at each volume control block until we find one that
  435.     // uses our refnum, or run out of VCBs.
  436.     index = 1; // start with first VCB
  437.     do {
  438.         pb.ioNamePtr = name; // Get the name
  439.         pb.ioVolIndex = index++;
  440.         anErr = PBHGetVInfo((HParmBlkPtr) &pb, false);
  441.     } while ((anErr == 0) && (pb.ioVDRefNum != (*thisDCE)->dCtlRefNum));
  442.     
  443.     // If we fell out because of an error, assume that it was because
  444.     // we ran out of VCBs. 
  445.     if (anErr != noErr) {
  446.         name[0] = 0; // clear the name
  447.         return 0;
  448.     }
  449.     
  450.     // We succeeded in finding a refnum - return it.
  451.     return pb.ioVRefNum;
  452. }
  453.  
  454.  
  455. //
  456. // Here's the vendor info we're s'posed to match to validate a drive
  457. //
  458. void SetValidationInfo(Str255 vendorString, Str255 productString, Str255 revisionString) {
  459.     PLstrcpy(gVendorString, vendorString);
  460.     PLstrcpy(gProductString, productString);
  461.     PLstrcpy(gRevisionString, revisionString);
  462. }
  463.  
  464. //
  465. // Find the next eligible device in the chain, and note its SCSI ID in gCurrentDevice.
  466. // Return 0 if we found a different one than the current one, or:
  467. //        kNoOtherValidDrives: if the only drive we could find is the current one.
  468. //
  469. OSErr NextEligibleDevice() {
  470.     short thisDeviceState;
  471.     SCSIAddress sentinalDevice, thisDevice; // remember the previous current device
  472.     
  473.     // We're going to loop until we find a new good device, or until we get back to
  474.     // where we started. If we're starting from having no valid device, we'll skip out when
  475.     // after we've tried ID 6.
  476.     sentinalDevice = (gCurrentDevice == kInvalidDevice) ? 6 : gCurrentDevice;
  477.     thisDevice = sentinalDevice; // we'll start by checking the one after this one.
  478.     
  479.     // Loop through the chain until we find a good device, or until we hit our
  480.     // sentinal device again.
  481.     do {
  482.         if (++thisDevice == 7) 
  483.             thisDevice = 0; // wrap around the SCSI chain
  484.         
  485.         thisDeviceState = CheckSCSIDevice(thisDevice); // try this device
  486.     } while ((thisDeviceState != noErr)    && (thisDevice != sentinalDevice));
  487.     
  488.     // We fell out of the loop, either because we got back to our original device,
  489.     // or because we found another good one (Note that there's a possibility that
  490.     // we got back to our original device and it was gone - that's why we test for
  491.     // error first here).
  492.     if (thisDeviceState == noErr) { // we found something good.
  493.         if (thisDevice != gCurrentDevice) { // it's different that the last device!
  494.             gCurrentDevice = thisDevice; // remember it
  495.             return noErr; // return successfully.
  496.         } else
  497.             return kNoOtherValidDrives; // the only good one we found is the current one.
  498.     } else
  499.         return thisDeviceState; // there weren't any valid drives - pass on the last SCSI error.
  500. }
  501.  
  502. //
  503. // Test the current SCSI device.
  504. // This routine assumes that the current device is one of our devices. The caller should 
  505. // put up whatever message is appropriate ("Testing…") before calling this routine. 
  506. //
  507. OSErr TestCurrentDevice() {
  508.     return noErr;
  509. }
  510.  
  511. //
  512. // Format the current SCSI device.
  513. // This routine assumes that the current device is one of our devices, and that it's OK with
  514. // the user that we trash it. The caller should put up whatever message is appropriate
  515. // ("Formatting…") before calling this routine. 
  516. //
  517.  
  518. OSErr FormatCurrentDevice() {
  519.     return (FormatSCSIDevice(gCurrentDevice));
  520. }
  521.  
  522.  
  523. //
  524. // Format a particular SCSI device. This routine could be used to define a special
  525. // SCSI address and format the existing driver at this address. FormatCurrentDevice
  526. // is calling this lower level routine. 
  527. //
  528.  
  529. OSErr FormatSCSIDevice(SCSIAddress device) {
  530.     unsigned char data;
  531.     unsigned short interleave;
  532.     SCSICommandBlock cmd;
  533.     short anErr;
  534.     
  535.     // Some drives, like the Quantum drive we're formatting, require a specific data
  536.     // pattern to be written during formatting. We set that here:
  537.     data = 0x6C; 
  538.     
  539.     // We also control the interleave pattern to be used. In our case we are 
  540.     // defining a hard coded value for Quantum drives. Set alternate values here:
  541.     interleave = 1;                //*** Are we going to change this?
  542.     
  543.     // Fill in the SCSI command block
  544.     StuffSCSICommandBlock(cmd, SCSICmd_Format, 0, data, interleave >> 8, interleave, 0);
  545.                                 //*** What are these 0's for? Pass constants.
  546.     // Do the format
  547.     anErr = SCSIOp(cmd, sizeof(SCSICommandBlock), device, kFormatTimeout,
  548.                  kNoBuffer);
  549.     
  550.     return anErr;
  551. }
  552.  
  553. //
  554. // Load the driver into a nonrelocatable block in the system heap; we get it out
  555. // of a resource of type 'SCSI', name "DTS_SCSI" and copy it into a pointer block
  556. // in the system heap, returning the pointer.  A nil return value signifies an
  557. // error.
  558. //
  559. OSErr
  560. LoadDriver(Ptr *driver,unsigned long *size)
  561. {    Handle            driverHandle;
  562.     Ptr                driverInstance;
  563.     unsigned long    driverSize;
  564.     
  565.     *driver = 0;
  566.     *size = 0;
  567.     
  568.     // Load the driver resource
  569.     driverHandle = Get1NamedResource('SCSI', "\pDTS_SCSI");
  570.     assert(driverHandle,"no driver loaded!");
  571.     if (!driverHandle)
  572.         return ResError();
  573.     
  574.     driverSize = GetHandleSize(driverHandle);
  575.     
  576.     // The driver must be in a Ptr block in the system heap
  577.     driverInstance = NewPtrSys(driverSize);
  578.     assert(driverInstance,"unable to allocate driver storage");
  579.     if (!driverInstance)
  580.         return MemError();
  581.     
  582.     // Copy the driver to the pointer block
  583.     BlockMove(*driverHandle,driverInstance,driverSize);
  584.     
  585.     ReleaseResource(driverHandle);
  586.     
  587.     *driver = driverInstance;
  588.     *size = driverSize;
  589.     
  590.     return noErr;
  591. }
  592.  
  593. //
  594. // Calculate the checksum using the algorithm described on IM V, pp 580-581
  595. //
  596. // This could be written to be significantly faster in assembly, or even by
  597. // using obscure tricks to fool the compiler into producing more optimal code,
  598. // but it's only used once for each format or update, and it's just not
  599. // worth it for the miniscule overall gain we would realize.
  600. //
  601.  
  602. unsigned short
  603. FigureChecksum(Ptr data,unsigned long length)
  604. {    register unsigned char        *dataPtr;
  605.     register unsigned long        remaining;
  606.     register unsigned short        sum;
  607.     
  608.     remaining = length;
  609.     dataPtr = data;
  610.     sum = 0;
  611.     
  612.     // Loop for each of the characters, adding it in and rotating the checksum
  613.     while (remaining--)
  614.     {    sum += *(dataPtr++);
  615.     
  616.         // Rotate left one bit; we have to manually rotate the top bit
  617.         //  around to the bottom because C only has a shift operator
  618.         
  619.         if (sum & 0x8000)
  620.             sum = (sum << 1) | 1;
  621.         else
  622.             sum = sum << 1;
  623.     }
  624.     
  625.     if (sum == 0)
  626.         sum = 0xFFFF;
  627.     
  628.     return sum;
  629. }
  630.     
  631. //
  632. // Fill in a partition map entry.
  633. //
  634. // This only fills in those fields that are used in most partitions and 
  635. // assumes that its storage has been pre-initialized to zero.  (We don't
  636. // fill in fields which would always be 0.)
  637. //
  638.  
  639. void
  640. InitPartitionMapEntry(Partition *p, unsigned long startBlock, unsigned long partLength,
  641.                         char *partName, char *partType, unsigned short partStatus)
  642. {
  643.     p->pmSig = 0x504D;                        // Signature is always $504D ('PM')
  644.     p->pmMapBlkCnt = kPartitionCount;        // Total number of partition map blocks
  645.     p->pmPyPartStart = startBlock;            // The first block in the partition
  646.     p->pmPartBlkCnt = partLength;            // Number of blocks in the partition
  647.     
  648.     assert(strlen(partName) <= 32,"Partition name is too long");
  649.     assert(strlen(partType) <= 32,"Partition type is too long");
  650.     
  651.     strncpy(p->pmPartName,partName,32);        // Copy 32 characters at most (handles
  652.     strncpy(p->pmParType,partType,32);        // not adding a nil if length == 32)
  653.     
  654.     p->pmDataCnt = partLength;                // Number of blocks devoted to data
  655.     p->pmPartStatus = partStatus;            // Status information
  656. }
  657.  
  658. //
  659. // This function is a catch-all for doing all the tasks to set up a disk:
  660. //  1) Setting up & writing out the disk structures, including the partition maps
  661. //  2) Writing out the driver into the driver partition
  662. //  3) Loading the now formatted drive's driver
  663. //  4) Calling DIZero to put the HFS structures onto the partition
  664. //
  665. // *** Think about splitting this up into more manageable chunks.  It's HUGE!
  666. //     (although, in all modesty, HDSetup is worse).
  667. //
  668.  
  669. #define kPhysicalBlockSize        512
  670.  
  671. OSErr
  672. PartitionCurrentDevice()
  673. {    OSErr                err;
  674.     Block0                *ddm;                // The image of our driver descriptor map
  675.     Partition            *partitionMap;        // Our partition map
  676.     Ptr                    driver;                // The driver instance
  677.     Ptr                    driverPartition;    // Storage for constructing the driver partition
  678.     unsigned long        deviceBlocks;        // The size of our device, in blocks
  679.     unsigned long        driverSize;            // The size of our driver, in bytes
  680.     unsigned long        driverBlocks;        // The size of our driver, in bytes
  681.     unsigned short        drvrChecksum;        // Our driver's checksum
  682.     unsigned long        blockCount;            // How many blocks for SCSIPrime to write
  683.     short                driveNum;            // The drive number for our now formatted device
  684.     unsigned char        volName[32];        // The name to give to the new volume
  685.     
  686.     // Because the driver's size is needed to set up the driver descriptor map,
  687.     // we'll start by loading it and figuring its checksum.
  688.     
  689.     err = LoadDriver(&driver,&driverSize);
  690.     if (err || !driver)
  691.     {    if (err)
  692.             return err;
  693.         else
  694.             return resNotFound;                // Sometimes the resource manager punts on errors
  695.     }
  696.     
  697.     driverBlocks = (driverSize + kPhysicalBlockSize - 1) / kPhysicalBlockSize;
  698.     assert(driverBlocks <= kDriverSpace,"kDriverSpace isn't enough room for the driver");
  699.     
  700.     drvrChecksum = FigureChecksum(driver,driverSize);
  701.     
  702.     // Next, we're going to need to figure out our geometry.  The following
  703.     // things are given:
  704.     //        1 block for the driver descriptor map in block 0.
  705.     //        kPartitionCount blocks for the partition map entries (currently 3):
  706.     //            1 for the partition map itself
  707.     //            1 for the driver partition
  708.     //            1 for the actual HFS partition
  709.     //        kDriverSpace blocks for the driver partition (currently 20)
  710.     //
  711.     // the rest can be given to the HFS partition.  Thus, the usable space for
  712.     // the HFS partition is equal to:
  713.     //        
  714.     //        deviceSize - 1 - kPartitionCount - kDriverSpace;
  715.     //
  716.     // Because kDriverSpace is currently 20 blocks (10 K), this means we're
  717.     // reserving 24 blocks (12 K) for our nefarious purposes.
  718.     //
  719.     // kDriverSpace is larger than it needs to be (the driver is only 3422 bytes
  720.     // as of this writing), but reserving a few extra K is worth it for future
  721.     // expansion, given the cost (7 K is not a lot of disk space) against the
  722.     // risk (having to reformat the disk to grow the driver partition at some
  723.     // point in the future because we didn't reserve enough space).  It's my
  724.     // guess that 10 K could accommodate a substantially more complex driver
  725.     // than I plan on writing at any point in the forseeable future, so this
  726.     // should do just fine.
  727.     //
  728.     // This also implies that the partition map starts at block #1 (zero-based),
  729.     // the driver starts at block #(1+kPartitionCount) and the HFS partition
  730.     // starts at block #(1+kPartitionCount+kDriverSpace).
  731.     
  732.     deviceBlocks = SCSIDoReadCapacityCommand(gCurrentDevice);
  733.     
  734.     if (deviceBlocks == 0)            // ReadCapacity indicates errors with a 0.
  735.     {    DisposePtr(driver);
  736.         return ioErr;
  737.     }
  738.     
  739.     // First we fill in the driver descriptor map, or "block 0".  This is
  740.     // described in Inside Mac V, page 577.  We've only got one driver
  741.     // (thank God, one is more than enough), so we can use the basic
  742.     // Block0 structure from SCSI.h.
  743.     
  744.     // The ddm occupies exactly one block, so that's what we allocate
  745.     ddm = (Block0*)NewPtrClear(kPhysicalBlockSize);
  746.     
  747.     assert(ddm,"Couldn't allocate room for the ddm");
  748.     if (!ddm)
  749.     {    err = MemError();
  750.         DisposePtr(driver);            // Release the driver's storage
  751.         return err;
  752.     }
  753.     
  754.     ddm->sbSig = 0x4552;                        // always $4552 ('EQ')
  755.     ddm->sbBlkSize = kPhysicalBlockSize;        // the block size of the device
  756.     ddm->sbBlkCount = deviceBlocks;                // the number of blocks on the device
  757.     ddm->sbDevType = 1;                            // used internally
  758.     ddm->sbDevId = 1;                            // used internally 
  759.     ddm->sbData = 0;                            // used internally 
  760.     ddm->sbDrvrCount = 1;                        // number of driver descriptors 
  761.     ddm->ddBlock = 1 + kPartitionCount;            // the first block of the SCSI driver (right after the p-map) 
  762.     ddm->ddSize = driverBlocks;                    // The size of the driver, rounded up to the next block
  763.     ddm->ddType = 1;                            // the System type (1 means Macintosh)
  764.     
  765.     // Now we write out the header block.  I thought about holding off on doing any
  766.     // writing until the entire set of information had been accumulated, but
  767.     // decided the minor inconvenience of occasionally having a partially-formatted
  768.     // disk wasn't enough to outweigh the convenience of having this function be
  769.     // well-partitioned into individual steps.
  770.     
  771.     // Write 1 block of data taken from ddm to the current device, unit #0,
  772.     // starting at block #0, using the standard block size, using a blind
  773.     // transfer, with our standard I/O length timeout.
  774.     
  775.     blockCount = 1;
  776.     err = SCSIPrime(ddm, CurrentDevice(), 0, 0, &blockCount, kPhysicalBlockSize,
  777.                     kDoWrite, kDoBlind, kIOTimeout);
  778.     
  779.     assert(!err,"Couldn't write out block 0");
  780.     
  781.     DisposePtr((Ptr)ddm);        // Whether the write worked or not, we're done with this
  782.     
  783.     if (err)
  784.     {    DisposePtr(driver);
  785.         return err;
  786.     }
  787.     
  788.     // Now we're going to set up the partition map.  We're only going to
  789.     // allocate & use 3 partition map entries because we don't support multiple
  790.     // partitions of any kind; if we did, we'd probably allocate a few extra
  791.     // to give us room to repartition later.
  792.     // 
  793.     // The three entries are, in turn, the entry for the map itself, then for the
  794.     // driver, then for the HFS partition.  (The order isn't important.)
  795.     
  796.     // Because we're making an array of several Partition structures, and we're
  797.     // then going to write them all out at once, it's important that they each be
  798.     // exactly the right length so they'll line up with the block boundaries
  799.     // correctly.  We can do this safely because the Partition structue has padding
  800.     // at the end to make it exactly 512 bytes long.  Were this not true, we would
  801.     // have to allocate and refer to our array differently.
  802.     
  803.     assert(sizeof(Partition) == kPhysicalBlockSize,"Partition size is wrong for use in array");
  804.     
  805.     partitionMap = (Partition*)NewPtrClear(kPartitionCount * kPhysicalBlockSize);
  806.     
  807.     assert(partitionMap,"Couldn't allocate room for the partition map");
  808.     
  809.     if (!partitionMap)
  810.     {    err = MemError();
  811.         DisposPtr(driver);
  812.         return err;
  813.     }
  814.     
  815.     // Fill in the entry for the partition map itself
  816.     // It starts at block 1 and goes for kPartitionCount blocks,
  817.     // and has the names as described on page 580 of Inside Mac V.
  818.     // Its status byte is binary 00010011 or hex $13
  819.     // (See IM V-581 for a description of all the bit flags; this has
  820.     // only allowsReading + isAllocated + isValid set)
  821.     
  822.     // *** Remember to check on correctness / necessity of status flags.
  823.     
  824.     InitPartitionMapEntry(&partitionMap[0],1,kPartitionCount,
  825.                             "Apple","Apple_partition_count",0x13);
  826.     
  827.     // Now the entry for the driver
  828.     // It starts at block 1 + kPartitionCount and goes for kDriverSpace blocks.
  829.     // Its status is binary 01011011 or hex $5B; relocatableCode +
  830.     // allowsReading + hasBootInfo + isAllocated + isValid.
  831.     
  832.     InitPartitionMapEntry(&partitionMap[1],1+kPartitionCount,kDriverSpace,
  833.                             "MacOS","Apple_Driver",0x5B);
  834.     
  835.     // In addition to the common stuff done by InitPartitionMapEntry,
  836.     // the entry for the device driver needs some more info:
  837.     
  838.     partitionMap[1].pmBootSize = driverSize;        // The actual size of the driver code
  839.     partitionMap[1].pmBootCksum = drvrChecksum;        // The checksum of the driver
  840.     strcpy(partitionMap[1].pmProcessor,"68000");    // The target processor
  841.     
  842.     // Now the entry for the HFS partition
  843.     // It starts at block 1 + kPartitionCount + kDriverSpace and goes for
  844.     // deviceSize - 1 - kPartitionCount - kDriverSpace blocks.
  845.     // Its status is binary 00110011 or hex $33; allowsWriting + allowsReading +
  846.     // isAllocated + isValid.
  847.     
  848.     InitPartitionMapEntry(&partitionMap[2],1+kPartitionCount+kDriverSpace,
  849.                             deviceBlocks-1-kPartitionCount-kDriverSpace,
  850.                             "MacOS","Apple_HFS",0x33);
  851.     
  852.     // Now we'll write out the partition map
  853.     
  854.     // Write out kPartitionCount blocks starting at block 1 from the data at
  855.     // address partitionMap to the current SCSI device, unit #0, using a blind
  856.     // transfer, with the standard I/O timeout
  857.     
  858.     blockCount = kPartitionCount;
  859.     err = SCSIPrime(partitionMap, CurrentDevice(), 0, 1, &blockCount,
  860.                 kPhysicalBlockSize, kDoWrite, kDoBlind, kIOTimeout);
  861.     
  862.     assert(!err,"Couldn't write out the partition map");
  863.     
  864.     // I would normally dispose of the partition maps here, but we'll need them
  865.     // to supply to the driver, down below, so I've got to save them
  866.     
  867.     if (err)
  868.     {    DisposePtr(driver);
  869.         DisposePtr((Ptr)partitionMap);
  870.         return err;
  871.     }
  872.     
  873.     // Now we'll write out the driver.  For purist reasons, I'm going to fill up
  874.     // the extra room in the partition with zeros.  This isn't necessary, but
  875.     // fills a deep-seated Freudian need within me.  Because we can only write
  876.     // out data in block increments, I can't write out the driver, then the
  877.     // zeros in two steps, because it's unlikely that the driver will fall exactly
  878.     // on a block boundary.  Theoretically, I could do it all with just a one
  879.     // block size buffer to deal with the boundary and to write the zeros out of,
  880.     // but the entire driver partition is only 10K, so I'll just take the cheap way
  881.     // out and construct the whole thing, then write it out.
  882.     
  883.     // Allocate using NewPtrClear to guarantee padding zeros
  884.     driverPartition = NewPtrClear(kDriverSpace * kPhysicalBlockSize);
  885.     
  886.     if (!driverPartition)
  887.     {    err = MemError();
  888.         DisposePtr(driver);
  889.         DisposePtr((Ptr)partitionMap);
  890.         return err;
  891.     }
  892.     
  893.     // Copy the driver into its partition room
  894.     BlockMove(driver,driverPartition,driverSize);
  895.     
  896.     // Now we write it out
  897.     
  898.     // Write out kDriverSpace blocks starting at block 1+kPartitionCount with
  899.     // all the usual stuff
  900.     
  901.     blockCount = kDriverSpace;
  902.     err = SCSIPrime(driverPartition, CurrentDevice(), 0, 1+kPartitionCount,
  903.                 &blockCount, kPhysicalBlockSize, kDoWrite, kDoBlind, kIOTimeout);
  904.     
  905.     assert(!err,"Couldn't write out driver partition");
  906.     
  907.     DisposePtr(driverPartition);        // Regardless, we're done with this now
  908.     
  909.     if (err)
  910.     {    DisposePtr(driver);
  911.         DisposePtr((Ptr)partitionMap);
  912.         return err;
  913.     }
  914.     
  915.     // OK, the drive should now be fully set up; all that remains is to get
  916.     // the driver to install itself and zero out the HFS stuctures with DIZero
  917.     
  918.     // First, we'll call the driver so it can install itself; it needs to know
  919.     // which SCSI device it is in charge of, its default data, and a pointer to
  920.     // the partition map for the HFS partition.
  921.     
  922.     CallDriver(driver, CurrentDevice(), 0, &partitionMap[2]);
  923.     
  924.     DisposePtr((Ptr)partitionMap);        // Now we're done with this
  925.  
  926.     // We check to see if it has successfully done its thing by seeing if it
  927.     // has installed a drive in the drive queue
  928.     
  929.     driveNum = DriveFromDevice(CurrentDevice());
  930.     
  931.     assert(driveNum != 0,"Driver didn't install a drive");
  932.     
  933.     if (driveNum == 0)            // 0 means no drive means an error means uh-oh
  934.     {    DisposePtr(driver);
  935.         return ioErr;            // to pick an error at random *** Better Error?
  936.     }
  937.     
  938.     // Now the driver is irretrievably linked into the OS, so we're gonna let it lie
  939.     
  940.     // Now that we're this far along, the volume is ready to recieve the HFS
  941.     // formatting (a blank directory, setting up the catalog file, etc.)
  942.     
  943.     // Get the default volume name from the 'STR#' resource
  944.     GetIndString(volName,kStringList,kVolumeNameString);
  945.     
  946.     // Create all the HFS info
  947.     err = DIZero(driveNum,volName);
  948.     
  949.     assert(!err,"DIZero returned an error");
  950.     
  951.     return err;
  952. }
  953.  
  954. //
  955. // FindPartitionEntry
  956. // Finds a partition that matches a given name and type and returns its entry and
  957. // its index into the partition map entry (physical block # = entry+1)
  958. // If name or type is nil, it is ignored; any name or type will match.  Otherwise,
  959. // pass C-style strings (null-terminated) and they'll be compared case-insensitively
  960. //
  961.  
  962. OSErr
  963. FindPartitionEntry(Partition *p,unsigned long *block,unsigned char *name,unsigned char *type)
  964. {    short            entry;
  965.     unsigned long    blockCount;
  966.     char            string[33];
  967.     Boolean            equal;
  968.     OSErr            err;
  969.     
  970.     assert(sizeof(*p) == kPhysicalBlockSize,"Partition struct is not the same size as a disk block");
  971.     
  972.     entry = 0;
  973.     
  974.     do
  975.     {    blockCount = 1;
  976.         err = SCSIPrime(p,CurrentDevice(), 0, entry+1, &blockCount, kPhysicalBlockSize,
  977.                     kDoRead, kDoBlind, kIOTimeout);
  978.         
  979.         assert(err == noErr,"Couldn't read partition map entry");
  980.         if (err)
  981.             return err;
  982.         
  983.         if (name == 0)
  984.             equal = true;            // A nil name pointer matches anything
  985.         else
  986.         {    // I have to go through various contortions because the name & type may be
  987.             // up to 32 characters long, and if they're exactly that long, they're not
  988.             // null terminated, so I can't use standard string manipulation routines
  989.             // I want to use EqualString to do a case-insensitive string compare, so
  990.             // I have to convert both arguments to pascal strings.
  991.             
  992.             BlockMove(p->pmPartName,string,32);        // Copy the 32 bytes of the name
  993.             string[32] = '\0';                        // Null-terminate if it's exactly 32 bytes long
  994.             c2pstr(string);                            // Convert it to pascal format
  995.             c2pstr(name);                            // Convert the name argument
  996.             
  997.             // Do a case-insensitive compare
  998.             equal = EqualString(string,name,false,true);
  999.         
  1000.             p2cstr(name);                            // Convert the name back
  1001.         }
  1002.         
  1003.         // If we got a match, try the same thing for the type
  1004.         if (equal)
  1005.         {    if (type == 0)
  1006.                 equal = true;        // A nil type pointer matches anything
  1007.             else
  1008.             {    BlockMove(p->pmParType,string,32);    // Copy the 32 bytes of the name
  1009.                 string[32] = '\0';                    // Null-terminate if it's exactly 32 bytes long
  1010.                 c2pstr(string);                        // Convert it to pascal format
  1011.                 c2pstr(type);                        // Convert the name argument
  1012.                 
  1013.                 // Do a case-insensitive compare
  1014.                 equal = EqualString(string,type,false,true);
  1015.                 
  1016.                 p2cstr(type);                        // Convert the name back
  1017.             }
  1018.             
  1019.             // If they both match, fill in the block and return;
  1020.             //    the partition struct is already filled in
  1021.             if (equal)
  1022.             {    *block = entry + 1;
  1023.                 return noErr;
  1024.             }
  1025.         }
  1026.         
  1027.         entry++;                            // Advance to the next partition map entry
  1028.     } while (entry < p->pmMapBlkCnt);        // Until we've done all the entries
  1029.     
  1030.     assert(false,"Couldn't find a matching partition entry");
  1031.     
  1032.     // If we get here, we didn't find a matching entry; return an error
  1033.     // *** I should select a more appropriate error
  1034.     
  1035.     return ioErr;
  1036. }
  1037.  
  1038. //
  1039. // Update the current device with the newest driver, then remount the volume
  1040. //
  1041. // This requires reading the driver descriptor map and updating it,
  1042. // finding the driver partition, making sure it's large enough and
  1043. // writing out the new driver, updating the driver descriptor,
  1044. // then finding the HFS partition and remounting the device.
  1045. //
  1046. // *** I should probably make sure the drive in question is being handled by
  1047. // our driver.  
  1048. //
  1049.  
  1050. OSErr
  1051. UpdateCurrentDevice()
  1052. {    OSErr            err;
  1053.     unsigned long    blockCount;        // Transfer length for use by SCSIPrime
  1054.     Block0            ddm;            // The driver descriptor map for the device
  1055.     Partition        pme;            // A partition map entry
  1056.     unsigned long    pmeBlock;        // Block number of the partition map entry
  1057.     unsigned long    drvrSize;        // driver size
  1058.     unsigned short    drvrBlockSize;    // driver size in blocks
  1059.     unsigned short    drvrChksum;        // driver checksum
  1060.     Ptr                driver;            // pointer to loaded driver
  1061.     unsigned long    drvrPartSize;    // size of the driver's partition
  1062.     Ptr                driverOut;        // pointer to disk image of driver partition
  1063.     short            driveNum;        // Driver's new drive number
  1064.     
  1065.     // Make sure ddm and pme are the right size to hold one disk block
  1066.     assert(sizeof(ddm) == kPhysicalBlockSize,"Block0 struct ddm is not the same size as a disk block");
  1067.     
  1068.     // Read in block 0
  1069.     // Read one block into ddm starrting from block 0
  1070.     blockCount = 1;
  1071.     err = SCSIPrime(&ddm, CurrentDevice(), 0, 0, &blockCount, kPhysicalBlockSize,
  1072.                 kDoRead, kDoBlind, kIOTimeout);
  1073.     
  1074.     assert(err == noErr,"Failed to read block 0 from drive");
  1075.     
  1076.     if (err)
  1077.         return err;
  1078.     
  1079.     // Now we're going to go looking for the driver's partition entry
  1080.     // Find a partition entry of name "MacOS" and type "Apple_Driver"
  1081.     // on the current device, starting with the first entry
  1082.     
  1083.     err = FindPartitionEntry(&pme,&pmeBlock,"MacOS","Apple_Driver");
  1084.     
  1085.     assert(err == noErr,"Couldn't find driver partition");
  1086.     
  1087.     if (err)
  1088.         return err;
  1089.     
  1090.     // If we've gotten this far, we should go and get a copy of the driver
  1091.     // we'll be installing on the disk and get its checksum.
  1092.     
  1093.     err = LoadDriver(&driver,&drvrSize);
  1094.     assert(err == noErr,"Couldn't load driver image");
  1095.     if (err)
  1096.         return err;
  1097.     
  1098.     drvrChksum = FigureChecksum(driver,drvrSize);
  1099.     drvrBlockSize = (drvrSize + kPhysicalBlockSize - 1) / kPhysicalBlockSize;
  1100.     
  1101.     // Now we make sure that the partition is large enough to hold the new
  1102.     // driver
  1103.     
  1104.     drvrPartSize = pme.pmPartBlkCnt * kPhysicalBlockSize;
  1105.     
  1106.     assert(drvrSize <= drvrPartSize,"Partition isn't big enough for driver");
  1107.     
  1108.     if (drvrSize > drvrPartSize)
  1109.     {    DisposePtr(driver);
  1110.         return kPartitionTooSmall;
  1111.     }
  1112.     
  1113.     // Because I'm picky, I like to fill up the extra space in the driver
  1114.     // partition with zeros, rather than leave garbage trailing it.  Because
  1115.     // the driver image I've loaded is in a fitted block in the system heap,
  1116.     // and we can only write 512-byte blocks, I've got to copy the driver
  1117.     // into another block to do this.  If this block can't be allocated
  1118.     // (for example, because the disk has a ten megabyte driver partition
  1119.     // and I can't allocate that much RAM), then I just bite the bullet and
  1120.     // write the mere minimum directly out of the system heap.  This means
  1121.     // that we will be leaving garbage on the disk; not only do I not erase
  1122.     // the previous driver completely, but I will be writing off of the end
  1123.     // of the driver in the system heap, causing some bits of other stuff to
  1124.     // be written with the driver.  This means we could have problems if
  1125.     // for some reason this area after our loaded driver (up to 511 bytes)
  1126.     // could not be accessed; if, for example, we were at the very upper 
  1127.     // boundary of RAM or if some segments of the system heap were made
  1128.     // inaccessible by a future version of the OS.  I'm going to take
  1129.     // that risk.                                ??? Maybe I shouldn't?    
  1130.     
  1131.     driverOut = NewPtrClear(drvrPartSize);
  1132.     
  1133.     assert(driverOut != 0,"Couldn't allocate partition write buffer");
  1134.     
  1135.     // If our allocation succeeds, copy the driver over
  1136.     if (driverOut)
  1137.         BlockMove(driver,driverOut,drvrSize);
  1138.     
  1139.     // Now we'll update our local copies of the driver descriptor map
  1140.     // and the partition map entry to reflect our new driver.  Very
  1141.     // few fields need to be changed; only those describing the actual
  1142.     // driver code, since the partition information remains the same.
  1143.     
  1144.     // First, update the ddm
  1145.     ddm.ddSize = drvrBlockSize;            // Size of the driver code in blocks
  1146.     ddm.ddType = 1;                        // The system type (1 for Mac)
  1147.     
  1148.     // Now, this partition's map entry
  1149.     pme.pmBootSize = drvrSize;            // Driver's size, in bytes
  1150.     pme.pmBootCksum = drvrChksum;        // Driver's checksum
  1151.     pme.pmBootAddr = 0;                    // Both of these fields should
  1152.     pme.pmBootEntry = 0;                    //  already be 0, just making sure
  1153.     strcpy(pme.pmProcessor,"68000");    // That good ol' 68K
  1154.     
  1155.     // Here begins the critical portion of the code; once we begin this
  1156.     // write, the disk is damaged until we do our last write.  Should
  1157.     // the power fail or the computer be crushed by a herd of stampeding
  1158.     // wildebeest while all this is going on, bad things could happen.
  1159.     // To minimize the risk, I write out the info in a particular order:
  1160.     // first, the driver itself; should this fail, at worst, the disk
  1161.     // will not boot (or will crash while trying to boot) and will need
  1162.     // to be re-updated.  Then the partition; while it is unlikely, should
  1163.     // the update fail in this step, damage to the entire disk is
  1164.     // possible, perhaps requiring reformatting.  Finally, the driver
  1165.     // descriptor map; this is likely to do the most damage if it fails
  1166.     // in an interesting way.  By moving the most-critical bits to the
  1167.     // end, I hope to minimize the chance of writing incorrect information
  1168.     // into these areas.
  1169.     
  1170.     // If I've succeeded in allocating my own block for the partition, then
  1171.     // write it out; otherwise, write the driver straight out of the system
  1172.     // heap.
  1173.         
  1174.     if (driverOut)
  1175.     {    blockCount = pme.pmPartBlkCnt;
  1176.         err = SCSIPrime(driverOut, CurrentDevice(), 0, pme.pmPyPartStart, &blockCount,
  1177.                     kPhysicalBlockSize, kDoWrite, kDoBlind, kIOTimeout);
  1178.         DisposePtr(driverOut);        // regardless of success in writing,
  1179.                                     //  we're done with this block
  1180.     }
  1181.     else
  1182.     {    blockCount = drvrBlockSize;    // just write out the driver code alone
  1183.         err = SCSIPrime(driver, CurrentDevice(), 0, pme.pmPyPartStart, &blockCount,
  1184.                     kPhysicalBlockSize, kDoWrite, kDoBlind, kIOTimeout);
  1185.     }
  1186.     
  1187.     assert(err == noErr,"SCSIPrime failed to write driver");
  1188.     
  1189.     if (err)
  1190.     {    DisposePtr(driver);
  1191.         return err;
  1192.     }
  1193.     
  1194.     // Next, the partition map entry
  1195.     blockCount = 1;
  1196.     err = SCSIPrime(&pme, CurrentDevice(), 0, pmeBlock, &blockCount,
  1197.                 kPhysicalBlockSize, kDoWrite, kDoBlind, kIOTimeout);
  1198.     
  1199.     assert(err == noErr,"SCSIPrime failed to write partition map entry");
  1200.     
  1201.     if (err)
  1202.     {    DisposePtr(driver);
  1203.         return err;
  1204.     }
  1205.     
  1206.     // Now, block 0
  1207.     blockCount = 1;
  1208.     err = SCSIPrime(&pme, CurrentDevice(), 0, 0, &blockCount,
  1209.                 kPhysicalBlockSize, kDoWrite, kDoBlind, kIOTimeout);
  1210.  
  1211.     assert(err == noErr,"SCSIPrime failed to write driver descriptor map");
  1212.  
  1213.     if (err)
  1214.     {    DisposePtr(driver);
  1215.         return err;
  1216.     }
  1217.     
  1218.     // OK, now everything's written out to the disk; all that remains is
  1219.     // to find the HFS partition and call the driver so it will mount itself.
  1220.     
  1221.     err = FindPartitionEntry(&pme,&pmeBlock,"MacOS","Apple_HFS");
  1222.     
  1223.     assert(err == noErr,"Couldn't find the HFS partition");
  1224.     
  1225.     if (err)
  1226.     {    DisposePtr(driver);
  1227.         return err;
  1228.     }
  1229.     
  1230.     // Call the driver; it takes care of installing itself and causing
  1231.     //  its device to be mounted.
  1232.     
  1233.     CallDriver(driver, CurrentDevice(), 0, &pme);
  1234.     
  1235.     // To see if it worked, make sure it created a drive queue entry
  1236.     
  1237.     driveNum = DriveFromDevice(CurrentDevice());
  1238.     
  1239.     assert(driveNum != 0,"Driver didn't install a drive");
  1240.     
  1241.     if (driveNum == 0)            // 0 means no drive means an error means uh-oh
  1242.     {    DisposePtr(driver);
  1243.         return ioErr;            // to pick an error at random *** Better Error?
  1244.     }
  1245.     
  1246.     return noErr;                // All was successful
  1247. }
  1248.  
  1249. //
  1250. // Mount the newly formatted and partitioned device.
  1251. // The driver's already been installed and opened, so everything
  1252. // is ready to insert the disk.
  1253. // 
  1254. OSErr MountCurrentDevice() {
  1255.     DCtlHandle        dce;
  1256.     TDriveVars        *vars;
  1257.     short            driveNum;
  1258.     
  1259.     dce = DCEFromDevice(gCurrentDevice);     //*** Is this being changed to use driveFromDevice?
  1260.     assert(dce,"DCE is nil at drive mounting time");
  1261.     if (!dce)
  1262.         return nsDrvErr;
  1263.         
  1264.     vars = *(TDriveVars **) (**dce).dCtlStorage;
  1265.     assert(vars,"DCtlStorage is nil at drive mounting time");
  1266.     if (!vars)
  1267.         return nsDrvErr;
  1268.     
  1269.     driveNum = vars->driveQElem.elem.dQDrive;
  1270.     
  1271.     PostEvent(diskEvt,driveNum);            //*** Comment this (just in case).
  1272.     
  1273.     return noErr;
  1274. }
  1275.  
  1276. //
  1277. // Dispose of a driver.  This is essentailly the same code as in ReplaceDriver()
  1278. // in DTS_SCSI_Install.c.
  1279. //
  1280.  
  1281. static void DisposDriver(Ptr driver)
  1282. {    long            offset;
  1283.     Ptr                driverBlock;
  1284.     
  1285.     offset = *(((long*)driver) - 1);            // Offset is one long back from driver
  1286.     driverBlock = ((Ptr)driver) - offset;        // Back up to start of block
  1287.     
  1288.     // Now that we've got the start of the block, we need to deallocate the pointer
  1289.     // block.  Theoretically, the driver could have been allocated in a locked
  1290.     // handle, but we're now requiring that anyone who loads arbitrary drivers
  1291.     // load them into a pointer block in the System heap.  Thus, we assume that
  1292.     // we've now got a pointer to a Ptr block, and we'll just dispose of it.
  1293.         
  1294.     DisposPtr(driverBlock);                        // Dispose of old driver block
  1295. }
  1296.  
  1297. //
  1298. // Unmount the volume on this partition, if we can.
  1299. //
  1300. OSErr UnmountCurrentDevice() {
  1301.     DCtlHandle        dce,driveDCE;
  1302.     int                volIndex;
  1303.     HVolumeParam    volPB;
  1304.     short            anErr;
  1305.     OSErr            err;
  1306.     DrvQEl            *drive;
  1307.     
  1308.     // Find the DCE for this SCSI device; it'll be NIL if there's
  1309.     // no open driver.
  1310.     dce = DCEFromDevice(gCurrentDevice);
  1311.     if (!dce)
  1312.         return noErr;
  1313.         
  1314.     // There's a driver here. Look through the volume queue until we
  1315.     // find a volume on this drive. If we don't find one, we're done.
  1316.     volIndex = 0;
  1317.     while (true) {
  1318.         // Get the next volume from the volume list
  1319.         volPB.ioVolIndex = ++volIndex;
  1320.         volPB.ioNamePtr = NULL;
  1321.         volPB.ioVDrvInfo = -1; // a wacky drive number
  1322.         anErr = PBHGetVInfo((HParmBlkPtr) &volPB, false);
  1323.         
  1324.         // If we ran out of volumes, it means there's no volume on this
  1325.         // device. We're done!
  1326.         if (anErr == nsvErr) // we ran out of volumes, so there's nothing to unmount
  1327.             return noErr;
  1328.         
  1329.         // If this volumes' driver refnum is ours, then this is our
  1330.         // volume.
  1331.         if ((anErr == noErr) && (volPB.ioVDRefNum == ~(32 + gCurrentDevice)))
  1332.             break;
  1333.     }
  1334.  
  1335.     // We found a volume on this device. Unmount it, if we can
  1336.     volPB.ioNamePtr = NULL;
  1337.     volPB.ioVRefNum = volPB.ioVDrvInfo; // copy the drive number
  1338.     if (err = PBUnmountVol((ParmBlkPtr) &volPB))
  1339.         return err;
  1340.     
  1341.     // Search the drive queue for another drive using this driver
  1342.     drive = (DrvQEl*)(GetDrvQHdr()->qHead);
  1343.     
  1344.     while (drive)
  1345.     {    driveDCE = GetDCtlEntry(drive->dQRefNum);
  1346.         // If the drivers match, return with noErr (we'll be killing no drivers today)
  1347.         if ((**driveDCE).dCtlDriver == (**dce).dCtlDriver)
  1348.             return noErr;
  1349.         drive = (DrvQEl*)drive->qLink;
  1350.     }
  1351.     
  1352.     // No drivers matched, so we should dispose of this one.    
  1353.     DisposDriver((**dce).dCtlDriver);
  1354. }